package com.atguigu.service.impl;

import com.atguigu.cache.ShopCache;
import com.atguigu.constant.RedisConst;
import com.atguigu.entity.ProductSalePropertyKey;
import com.atguigu.entity.SkuImage;
import com.atguigu.entity.SkuInfo;
import com.atguigu.exception.SleepUtils;
import com.atguigu.mapper.ProductSalePropertyKeyMapper;
import com.atguigu.mapper.SkuSalePropertyValueMapper;
import com.atguigu.service.SkuDetailService;
import com.atguigu.service.SkuImageService;
import com.atguigu.service.SkuInfoService;
import com.atguigu.service.SkuSalePropertyValueService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author ：Hachimen
 * @date ：Created in 2021/8/26 11:34
 * @description：service
 * @modified By：
 * @version: 0.0.1
 */
@Service
public class SkuDetailServiceImpl implements SkuDetailService {
    @Autowired
    private SkuInfoService skuInfoService;
    @Autowired
    private SkuImageService skuImageService;
    @Autowired
    private ProductSalePropertyKeyMapper productSalePropertyKeyMapper;
    @Autowired
    private SkuSalePropertyValueMapper skuSalePropertyValueMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    @ShopCache(prefix = RedisConst.SKUKEY_PREFIX)
    public SkuInfo getSkuInfo(Long skuId) {
        return getSkuInfoFromDB(skuId);
    }
    //redis锁+缓存
    private SkuInfo getSkuInfoFromRedis1(Long skuId){
        String skuKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;//SKU在redis中储存的key
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
        if(skuInfo == null){//缓存没有获取到
            //准备从数据库获取，先获取锁
            String uuid = UUID.randomUUID().toString();
            String skuLock = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;//SKU锁的key
            Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(skuLock, uuid, RedisConst.WAITTIN_GET_LOCK_TIME, TimeUnit.SECONDS);
            if(aBoolean){
                //获取到锁
                //从数据库中获取
                skuInfo = this.getSkuInfoFromDB(skuId);
                if(skuInfo == null){
                    //数据库未获取到
                    skuInfo = new SkuInfo();
                    redisTemplate.opsForValue().set(skuKey,skuInfo,RedisConst.SKUKEY_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);//返回空值给redis
                    return skuInfo;
                }
                //数据库获取到值
                redisTemplate.opsForValue().set(skuKey,skuInfo);//返回值给redis
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                redisScript.setResultType(Long.class);
                redisScript.setScriptText(script);
                redisTemplate.execute(redisScript, Arrays.asList(skuKey),uuid);//释放锁
                return skuInfo;
            }else{
                //未获取到锁
                SleepUtils.sleepMillis(30);
                this.getSkuInfo(skuId);//自旋
            }
        }
        return skuInfo;
    }


    //redisson锁+缓存
    private SkuInfo getSkuInfoFromRedis2(Long skuId){
        String skuKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;//SKU在redis中储存的key
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
        if(skuInfo == null){//缓存没有获取到
            //准备从数据库获取，先获取锁
            //String uuid = UUID.randomUUID().toString();
            String skuLock = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;//SKU锁的key
            //Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(skuLock, uuid, RedisConst.WAITTIN_GET_LOCK_TIME, TimeUnit.SECONDS);
            RLock lock = redissonClient.getLock(skuLock);
            try {
                boolean b = lock.tryLock(RedisConst.WAITTIN_GET_LOCK_TIME, RedisConst.LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
                //获取到锁
                //从数据库中获取
                if(b){
                    skuInfo = this.getSkuInfoFromDB(skuId);
                    if(skuInfo == null){
                        //数据库未获取到
                        skuInfo = new SkuInfo();
                        redisTemplate.opsForValue().set(skuKey,skuInfo,RedisConst.SKUKEY_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);//返回空值给redis
                        return skuInfo;
                    }
                    //数据库获取到值
                    redisTemplate.opsForValue().set(skuKey,skuInfo);//返回值给redis
                    lock.unlock();//释放锁
                    return skuInfo;
                }else{
                    SleepUtils.sleepMillis(30);
                    this.getSkuInfo(skuId);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return skuInfo;
    }

    //从数据库获取SKUINFO
    private SkuInfo getSkuInfoFromDB(Long skuId) {
        SkuInfo skuInfo = skuInfoService.getById(skuId);
        if(skuInfo != null){
            QueryWrapper<SkuImage> wrapper = new QueryWrapper<>();
            wrapper.eq("sku_id",skuId);
            List<SkuImage> skuImageList = skuImageService.list(wrapper);
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }

    @Override
    public List<ProductSalePropertyKey> getSkuSalePropertyKeyAndValue(Long productId, Long skuId) {
        return productSalePropertyKeyMapper.getSkuSalePropertyKeyAndValue(productId,skuId);
    }

    @Override
    public Map getSkuSalePropertyValueId(Long productId) {
        Map<String, String> hashMap = new HashMap<>();
        List<Map> mapList = skuSalePropertyValueMapper.getSkuSalePropertyValueId(productId);
        if(!CollectionUtils.isEmpty(mapList)){
            for (Map map : mapList) {
                hashMap.put(map.get("sale_property_value_id").toString(),map.get("sku_id").toString());
            }
        }
        return hashMap;
    }
}
